/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.telsoft.hms.lib.security.core;

import java.util.Collection;
import com.telsoft.hms.lib.security.exception.SessionNotExistsException;
import com.telsoft.hms.lib.security.exception.SessionRejectedException;

/**
 * @author HùngPV thanbotbeo10@gmail.com, hungpv@telsoft.com.vn
 * @author ViệtPH hongvietmtb@gmail.com, vietph@telsoft.com.vn Ngày tạo:
 *         09/09/2014
 */

// Quản lý Session, sau mỗi khoảng thời gian nhất định sẽ kiểm tra các Session
// đã bị hết hạn
// và remove đi
// Các phương thức chính:
// - getAllSessions
// - addSession
// - removeSession
// - getSession
// - updateSession
// Các hàm NÊN override:
// - validateNewSession

public abstract class AbstractSessionManager {

	private java.util.concurrent.ConcurrentHashMap<String, AbstractSession> mSessions;
	private Boolean mbCheckSessionTimeoutWhenGetSession = false;

	public AbstractSessionManager(int intervalCheckSessionTimeout) {
		this.mSessions = new java.util.concurrent.ConcurrentHashMap<String, AbstractSession>();
		this.miIntervalCheckSessionTimeout = intervalCheckSessionTimeout;
		startThreadCheckSessionTimeOut();
	}

	// bật chế độ kiểm tra session time out trước mỗi lần get ra session
	public synchronized void enableCheckSessionTimeoutWhenGetSession(
			boolean enabled) {
		mbCheckSessionTimeoutWhenGetSession = enabled;
	}

	// lấy ra collection tất cả các session hiện có
	public synchronized Collection<AbstractSession> getAllSessions() {
		return this.mSessions.values();
	}

	// thêm một session mới
	public synchronized void addSession(AbstractSession session)
			throws SessionRejectedException {
		try {
			validateNewSession(session);
			putSession(session);
		} catch (Exception ex) {
			if (ex instanceof SessionRejectedException) {
				throw ex;
			} else {
				throw new SessionRejectedException(ex.getClass().getName());
			}
		}
	}

	// kiểm tra xem session có hợp lệ hay không,
	// hàm này được gọi trước khi một Session được thêm vào danh sách quản lý
	// của AbstractSessionManager
	protected abstract void validateNewSession(AbstractSession abtractSession)
			throws SessionRejectedException;

	// xóa một session
	public synchronized void removeSession(String securityKey) {
		try {
			this.mSessions.remove(securityKey);
		} catch (NullPointerException npEx) {
		}
	}

	// lấy ra session theo securityKey
	public synchronized AbstractSession getSession(String securityKey)
			throws SessionNotExistsException {
		if (!checkSessionExists(securityKey)) {
			throw new SessionNotExistsException(securityKey);
		}
		AbstractSession session = this.mSessions.get(securityKey);
		if (mbCheckSessionTimeoutWhenGetSession) {
			if (session.isTimedOut()) {
				removeSession(session.getSecurityKey());
				throw new SessionNotExistsException(securityKey);
			}
		}
		return session;
	}

	// kiểm tra xem session có tồn tại không
	public synchronized boolean checkSessionExists(String securityKey) {
		return this.mSessions.containsKey(securityKey);
	}

	// Update session
	public synchronized void updateSession(AbstractSession session) {
		this.mSessions.remove(session.getSecurityKey());
		this.mSessions.put(session.getSecurityKey(), session);
	}

	// Kiểm tra và Xóa các session đã hết hạn (timed out)
	protected synchronized void removeTimedOutSessions() {
		if (this.mSessions.isEmpty()) // danh sách trống
		{
			return;
		}
		java.util.List<String> listSessionToRemove = new java.util.ArrayList<>();
		// danh
		// sách
		// sessions
		// cần
		// xóa,
		// dựa
		// vào
		// Security
		// Key
		// (String)
		// duyệt danh sách lấy ra từng phần tử để kiểm tra xem có phần tử nào
		// timed out thì thêm vào danh sách session cần xóa
		for (java.util.Map.Entry<String, AbstractSession> entry : this.mSessions
				.entrySet()) {
			try {
				AbstractSession session = entry.getValue();
				if (session.isTimedOut()) {
					listSessionToRemove.add(entry.getKey());
				}
			} catch (Exception ex) {
			}
		}
		if (!listSessionToRemove.isEmpty()) {
			// duyệt danh sách các session cần xóa và gọi phương thức xóa
			// session
			for (String timedOutSessionId : listSessionToRemove) {
				removeSession(timedOutSessionId);
			}
		}
	}

	// đếm số session hiện có
	public synchronized int countSessions() {
		return this.mSessions.size();
	}

	// <editor-fold desc="Tạo thread lặp và kiểm tra Session time out">
	private ThreadCountdownCheckTimedout threadCheckTimeout;
	private int miIntervalCheckSessionTimeout;

	private void startThreadCheckSessionTimeOut() {
		this.threadCheckTimeout = new ThreadCountdownCheckTimedout(
				miIntervalCheckSessionTimeout);
		this.threadCheckTimeout.addEventListener(new SessionEventAdapter() {
			@Override
			public void onCheckingTimedOut() {
				removeTimedOutSessions();
			}

			@Override
			public void threadCheckTimedOutWasDead() {
				startThreadCheckSessionTimeOut();
			}
		});
		this.threadCheckTimeout.start();
	}

	// </editor-fold>

	protected void putSession(AbstractSession session) {
		this.mSessions.put(session.getSecurityKey(), session);
	}
}
